package com.pankajm.gre;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Random;
import java.util.Set;

import com.pankajm.common.IoHelper;
import com.pankajm.common.Preconditions;
import com.pankajm.gre.WordGame.Mode;

public class WordListJumbler {
	private final List<WordItem> wordlist;
	private final List<WordItem> wordsRemaining;
	private final Set<WordItem> wordsIKnow;
	private final List<WordItem> wordsFiltered;
	private final Set<WordItem> incorrect;
	private final Random generator;
	private final int wlnumber;

	public WordListJumbler(List<WordItem> wordlist, int wlnumber) {
		this.wordlist = wordlist;
		this.wlnumber = wlnumber;
		this.wordsRemaining = new ArrayList<WordItem>(this.wordlist);
		this.wordsIKnow = new HashSet<WordItem>();
		this.wordsFiltered = new ArrayList<WordItem>(this.wordlist);
		this.incorrect = new HashSet<WordItem>();
		Collections.copy(wordsRemaining, wordlist);
		Collections.copy(wordsFiltered, wordlist);
		generator = new Random(System.currentTimeMillis());
	}
	
	public void presentNextWordItem(Mode mode) throws IOException {
		if (wordsRemaining.size() == 0) {
			System.out.println("No more words remaining in word list " + this.wlnumber 
				+ ". Reload again or load another wordlist.");
			return;
		}	
		
		int wordIndex = generator.nextInt(wordsRemaining.size());
		WordItem questionItem = wordsRemaining.remove(wordIndex);

		if (mode == Mode.LEARN) {
			System.out.println("Word [" + questionItem.getWord() + "]");
			System.out.println("Meaning [" + questionItem.getMeaning() + "]");
			return;
		} else if (mode == Mode.FILTER) {
			System.out.println("Word [" + questionItem.getWord() + "]");
			System.out.println("Press s to skip this word out (meaning you know this)");
			String initialResponse = IoHelper.readLine();
			System.out.println("Meaning [" + questionItem.getMeaning() + "]");
			System.out.println("Press s again to confirm");
			String confirmationResponse = IoHelper.readLine();
			if (initialResponse.trim().equalsIgnoreCase("s") && confirmationResponse.trim().equalsIgnoreCase("s")) {
				wordsIKnow.add(questionItem);
			}
		}
	}
	
	public int askJumbledQuery(int numChoices) throws IOException {
		Preconditions.checkArgument(numChoices > 0, 
		"Any question will have atleast 1 choice to choose from");

		if (wordsRemaining.size() == 0) {
			System.out.println("No more words remaining in word list " + this.wlnumber 
				+ ". Reload again or load another wordlist.");
				return 0;
		}
		
		if (numChoices > this.wordlist.size()) {
			numChoices = wordlist.size();
		}

		int wordIndex = generator.nextInt(wordsRemaining.size());
		WordItem questionItem = wordsRemaining.remove(wordIndex);
		System.out.println("Choose the right meaning of [" + questionItem.getWord() + "]");

		List<String> choices = prepareChoices(numChoices, questionItem);

		for (int i = 0; i < choices.size(); i++) {
			System.out.println("Option " + (i+1) + ". " + choices.get(i));
		}

		System.out.print("Enter option number : ");
		Integer optionalInt = IoHelper.readOptionalInt();
		if (optionalInt == null) {
			System.out.println("Skipped.");
			System.out.println("Correct answer is " + (choices.indexOf(questionItem.getMeaning()) + 1));
			
			incorrect.add(questionItem);
			return -1;
		} else {
			int choice = optionalInt;
			int index = choice - 1;

			// An invalid choice number implies skip the question.
			if (choice < 1 || choice > numChoices) {
				System.out.println("Skipped.");
				System.out.println("Correct answer is " + (choices.indexOf(questionItem.getMeaning()) + 1));
				
				incorrect.add(questionItem);
				return -1;
			}

			if (choices.get(index).equals(questionItem.getMeaning())) {
				System.out.println("Correct!");
				return 1;
			} else {
				/* A valid and incorrect answer */
				System.out.println("Correct answer is " + (choices.indexOf(questionItem.getMeaning()) + 1));
				wordsRemaining.add(questionItem);
				incorrect.add(questionItem);
				return -1;
			}
		}
	}

	public void summary(Mode currentMode) {
		System.out.println("Number of words remaining in current word list = " + wordsRemaining.size());

		if (currentMode == Mode.CHALLENGE) {
			if (incorrect.size() > 0) {
				System.out.println("Wrong resposes for following words:");
				for (WordItem wi : incorrect) {
					System.out.println(wi.getWord() + "-->" + wi.getMeaning());
				}
			}	
		} else if (currentMode == Mode.FILTER) {
			wordsFiltered.removeAll(wordsIKnow);
			if (wordsFiltered.size() > 0) {
				System.out.println("Filtered words:");
				for (WordItem wi : wordsFiltered) {
					System.out.println(wi.getWord() + "-->" + wi.getMeaning());
				}
			}	
		} else {
			// later add any more info applicable to learning mode.
		}
	}

	private List<String> prepareChoices(int numChoices, WordItem questionItem) {
		List<String> choices = new ArrayList<String>();

		choices.add(questionItem.getMeaning());

		while (choices.size() != numChoices) {
			String incorrectMeaning 
			= wordlist.get(generator.nextInt(wordlist.size())).getMeaning();
			if (!choices.contains(incorrectMeaning)) {
				choices.add(incorrectMeaning);	
			}
		}

		for (int i = 1; i <= (1 + generator.nextInt(999)); i++) {
			randomizeOrderOfList(choices);
		}
		return choices;
	}

	private void randomizeOrderOfList(List<String> list) {
		List<String> listPlaceHolder = new ArrayList<String>();

		while (!list.isEmpty()) {
			int i = generator.nextInt(list.size());
			listPlaceHolder.add(list.remove(i));
		}

		ListIterator<String> iterator = listPlaceHolder.listIterator();
		while (iterator.hasNext()) {
			list.add(iterator.next());
		}
	}
}
